package com.wx.pay.util;

import java.math.BigDecimal;
import java.net.URLEncoder;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;

import org.apache.commons.lang.RandomStringUtils;
import com.alibaba.fastjson.JSON;
import com.wx.pay.model.WechatPayVo;

/**
 * 微信支付工具类
  * @Title: WechatPayUtil.java 
  * @Description: TODO
  * @author vivi
  * @date 2016年2月17日 下午4:52:34 
  * @version V1.0
 */
public class WechatPayUtil {
	private static String TEMPLATE_HTML = "<html><form name=\"payForm\" action=\"%s\">%s</form><script>document.payForm.submit()</script></html>";
	private static String TEMPLATE_INPUT = "<input type=\"hidden\" name=\"%s\" value=\"%s\"/>";
	private static String RANDOM_STRING = "1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM";
	/** h5微信支付模板 **/
	public final static String TEMPLATE_H5_PAY_HTML = "<html >"+
			" <head>"+
			"  <meta charset=\"UTF-8\">"+
			"  <title>微信支付请求页面</title>"+
			"  <script>"+
			"	function onBridgeReady(){"+
			"	   WeixinJSBridge.invoke("+
			"		   'getBrandWCPayRequest', %s,"+
			"		   function(res){     "+
			"			   if(res.err_msg == \"get_brand_wcpay_request：ok\" ) {"+
			"				  window.location.href = \"%s\";"+ 
			"			   }else{"
			+ "			   	  window.location.href = \"%s\";"
			+ "			   }"+
			"		   }"+
			"	   ); "+
			"	}"+
			"	if (typeof WeixinJSBridge == \"undefined\"){"+
			"	   if( document.addEventListener ){"+
			"		   document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);"+
			"	   }else if (document.attachEvent){"+
			"		   document.attachEvent('WeixinJSBridgeReady', onBridgeReady); "+
			"		   document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);"+
			"	   }"+
			"	}else{"+
			"	   onBridgeReady();"+
			"	}"+
			"	</script>"+
			" </head>"+
			" <body></body>"+
			"</html>";

	/**
	 * 构建支付请求xml
	 * @param orderNum
	 * @param orderAmount
	 * @param orderName
	 * @param unionPayVo
	 * @return
	 */
	public static String buildPayXml(String orderNum, String orderAmount, String orderName,String tradeType, String openid, WechatPayVo wechatPayVo){
		Map<String,String> data = buildPayData(orderNum, orderAmount, orderName, tradeType, openid, wechatPayVo);
		return StringUtil.toXml(data, "xml");
	}
	
	/**
	 * 构建支付请求url
	 * @param orderNum
	 * @param orderAmount
	 * @param orderName
	 * @param unionPayVo
	 * @return
	 */
	public static String buildPayUrl(String orderNum, String orderAmount, String orderName,String tradeType, String openid, WechatPayVo wechatPayVo){
		Map<String,String> data = buildPayData(orderNum, orderAmount, orderName, tradeType, openid, wechatPayVo);
		String reuslt = StringUtil.toStringForUrl(wechatPayVo.getUnifiedorderUrl() , data);
		return reuslt; 
	}
	
	/**
	 * 构建支付请求自动提交表单html
	 * @param orderNum
	 * @param orderAmount
	 * @param orderName
	 * @param unionPayVo
	 * @return
	 */
	public static String buildPayHtml(String orderNum, String orderAmount, String orderName,String tradeType, String openid, WechatPayVo wechatPayVo){
		Map<String,String> data = buildPayData(orderNum, orderAmount, orderName, tradeType, openid, wechatPayVo);
		StringBuffer params = new StringBuffer();
		for(Entry<String, String> param : data.entrySet()){
			params.append(String.format(TEMPLATE_INPUT, param.getKey(), param.getValue()));
		}
		return String.format(TEMPLATE_HTML, wechatPayVo.getUnifiedorderUrl(), params.toString());
	}
	
	/**
	 * 构建支付参数
	 * @param orderNum
	 * @param orderAmount
	 * @param orderName
	 * @param unionPayVo
	 * @return
	 */
	public static Map<String,String> buildPayData(String orderNum, String orderAmount, String orderName, String tradeType, String openid, WechatPayVo wechatPayVo){
		Map<String,String> data = genBaseParamData(orderNum, orderAmount, orderName, tradeType, openid, wechatPayVo);
		data.remove("sign");
		String sign = signature(data, wechatPayVo.getSignatureKey());
		data = MapUtil.merge(LinkedHashMap.class, data, "sign", sign);
		return data;
	}
	
	/**
	 * 签名
	 * @param data
	 * @param signatureKey
	 * @return
	 */
	public static String signature(Map<String,String> data, String signatureKey){
		String signatureString = StringUtil.toStringForUrl(data);
		String signature = MD5Util.MD5Encode(signatureString+"&key="+signatureKey, "UTF-8").toUpperCase();
		return signature;
	}
	
	/**
	 * 获取基本参数数据
	 * @param orderNum
	 * @param orderAmount
	 * @param merReserve
	 * @param frontURL
	 * @param backURL
	 * @param merID
	 * @param acqID
	 * @return
	 */
	private static Map<String,String> genBaseParamData(String orderNum, String orderAmount, String merReserve, String tradeType, String openid, WechatPayVo wechatPayVo){
		Map<String,String> data = new TreeMap();
		if(WechatTradeTypeEnum.APP.getCode().equals(tradeType)){
			data.put("appid",wechatPayVo.getMobile_appid());
			data.put("mch_id",wechatPayVo.getMobile_mch_id());
		}else{
			data.put("appid",wechatPayVo.getAppid());
			data.put("mch_id",wechatPayVo.getMch_id());
		}
		data.put("nonce_str",getNonceStr(orderNum));
		//data.put("sign","");
		data.put("body",merReserve);
		data.put("attach",merReserve);
		data.put("out_trade_no",orderNum);
		data.put("fee_type","CNY");//设置成港币
		data.put("total_fee",orderAmount);
		data.put("spbill_create_ip",wechatPayVo.getSpbill_create_ip());
		//data.put("time_start","");
		//data.put("time_expire","");
		//data.put("goods_tag","");
		data.put("notify_url",wechatPayVo.getNotify_url());
		data.put("trade_type",tradeType);//JSAPI APP
		//data.put("product_id","");
		//data.put("limit_pay","");
		if(WechatTradeTypeEnum.JSAPI.getCode().equals(tradeType) ){
			data.put("openid", openid);
			data.put("device_info","WEB");
		}else if(WechatTradeTypeEnum.NATIVE.getCode().equals(tradeType)){
			data.put("product_id", orderNum);
			data.put("device_info","WEB");
			//data.put("time_start", StringUtil.nowYyyyMMddHHmmssString());
			//data.put("time_expire", StringUtil.nowYyyyMMddHHmmssString(7200000));
		}
		return data;
	}
	
	/**
	 * 生成授权请求url
	 * @param wechatPayVo
	 * @return
	 */
	public static String buildAuthorizeUrl(WechatPayVo wechatPayVo){
		Map<String,String> data = genAuthorizeParamData(wechatPayVo);
		String reuslt = StringUtil.toStringForUrl(wechatPayVo.getAuthorizeUrl() , data);
		return reuslt+"#wechat_redirect"; 
	}
	
	/**
	 * 生成授权请求参数
	 * @param wechatPayVo
	 * @return
	 */
	public static Map<String,String> genAuthorizeParamData(WechatPayVo wechatPayVo){
		Map<String,String> data = new TreeMap();
		data.put("appid", wechatPayVo.getAppid());
		data.put("redirect_uri", wechatPayVo.getAuthorizeRedirectUrl());
		data.put("response_type", "code");
		data.put("scope", "snsapi_base");
		data.put("state", "pay");
		data.put("from-timeline", "");
		data.put("isappinstalled", "0");
		return data;
	}
	
	
	/**
	 * 生成openid请求url
	 * @param wechatPayVo
	 * @return
	 */
	public static String buildOpenIdUrl(String code, WechatPayVo wechatPayVo){
		Map<String,String> data = genOpenIdParamData(code, wechatPayVo);
		String reuslt = StringUtil.toStringForUrl(wechatPayVo.getOpenidUrl() , data);
		return reuslt; 
	}
	
	/**
	 * 生成openid请求参数
	 * @param wechatPayVo
	 * @return
	 */
	public static Map<String,String> genOpenIdParamData(String code, WechatPayVo wechatPayVo){
		Map<String,String> data = new TreeMap();
		data.put("appid", wechatPayVo.getAppid());
		data.put("secret", wechatPayVo.getAppSecret());
		data.put("code", code);
		data.put("grant_type", "authorization_code");
		return data;
	}
	

	
	/**
	 * 生成微信H5支付html
	 * @param prepayId
	 * @param wechatPayVo
	 * @return
	 */
	public static String buildH5PayHtml(String prepayId, WechatPayVo wechatPayVo){
		String json = WechatPayUtil.buildH5PayJson(prepayId, wechatPayVo);
		return String.format(TEMPLATE_H5_PAY_HTML, json, wechatPayVo.getSuccessH5Url(), wechatPayVo.getErrorH5Url());
	}
	
	/**
	 * 生成微信H5支付json数据
	 * @param prepayId
	 * @param wechatPayVo
	 * @return
	 */
	public static String buildH5PayJson(String prepayId, WechatPayVo wechatPayVo){
		return JSON.toJSONString(buildH5PayData(prepayId, wechatPayVo)); 
	}
	
	/**
	 * 生成微信H5支付json数据
	 * @param prepayId
	 * @param wechatPayVo
	 * @return
	 */
	public static Map<String,String> buildH5PayData(String prepayId, WechatPayVo wechatPayVo){
		Map<String,String> data = genH5PayParamData(prepayId, wechatPayVo);
		data.remove("paySign");
		String paySign = signature(data, wechatPayVo.getSignatureKey());
		data.put("paySign", paySign);
		return data; 
	}
	
	/**
	 * 生成微信h5支付参数
	 * @param wechatPayVo
	 * @return
	 */
	public static Map<String,String> genH5PayParamData(String prepayId, WechatPayVo wechatPayVo){
		Map<String,String> data = new TreeMap();
		data.put("appId", wechatPayVo.getAppid());
		data.put("timeStamp", String.valueOf(System.currentTimeMillis()));
		data.put("nonceStr", RandomStringUtils.random(32,RANDOM_STRING).toUpperCase());
		data.put("package", "prepay_id="+prepayId);
		data.put("signType", "MD5");
		data.put("paySign", "");
		return data;
	}
	
	/**
	 * 生成异步通知返回xml
	 * @param code
	 * @param msg
	 * @return
	 */
	public static String buildNotifyReturnXml(String code, String msg){
		Map<String,String> data = genNotifyReturnData(code, msg);
		return StringUtil.toXml(data, "xml");
	}
	
	/**
	 * 生成异步通知返回数据
	 * @param wechatPayVo
	 * @return
	 */
	public static Map<String,String> genNotifyReturnData(String code, String msg){
		Map<String,String> data = new TreeMap();
		data.put("return_code", code);
		data.put("return_msg", msg==null?"":msg);
		return data;
	}
	
	
	/**
	 * 生成微信H5支付json数据
	 * @param prepayId
	 * @param wechatPayVo
	 * @return
	 */
	public static String buildAppPayJson(String prepayid,String packageNum, WechatPayVo wechatPayVo){
		Map<String,String> data = genAppPayData(prepayid,packageNum,wechatPayVo);
		data.remove("sign");
		String sign = signature(data, wechatPayVo.getSignatureKey());
		data = MapUtil.merge(LinkedHashMap.class, data, "sign", sign);
		return JSON.toJSONString(data); 
	}
	
	/**
	 * 生成微信mobile支付字符串
	 * @param prepayId
	 * @param wechatPayVo
	 * @return
	 */
	public static String buildAppPayString(String prepayid,String packageNum,WechatPayVo wechatPayVo){
		Map<String,String> data = genAppPayData(prepayid,packageNum,wechatPayVo);
		data.remove("sign");
		String sign = signature(data, wechatPayVo.getSignatureKey());
		data = MapUtil.merge(LinkedHashMap.class, data, "sign", sign);
		return JSON.toJSONString(data); 
	}
	
	/**
	 * 生成app支付数据
	 * @param prepayid
	 * @param wechatPayVo
	 * @return
	 */
	public static Map<String,String> genAppPayData(String prepayid,String packageNum, WechatPayVo wechatPayVo){
		Map<String,String> data = new TreeMap();
		data.put("appid", wechatPayVo.getMobile_appid());
		data.put("partnerid", wechatPayVo.getMobile_mch_id());
		data.put("prepayid", prepayid);
		data.put("package", "Sign=WXPay");
		//data.put("noncestr", RandomStringUtils.random(32, RANDOM_STRING).toUpperCase());
		data.put("noncestr", getNonceStr(packageNum));
		data.put("timestamp", String.valueOf(System.currentTimeMillis()).substring(0, 10));
		data.put("sign", "");
		return data;
	}
	
	/**
	 * 
	 * @param prepayid
	 * @param wechatPayVo
	 * @return
	 */
	public static String buildWapPayUrl(String prepayid, WechatPayVo wechatPayVo){
		Map<String,String> data = genWapPayData(prepayid, wechatPayVo);
		data.remove("sign");
		String sign = signature(data, wechatPayVo.getSignatureKey());
		data.put("sign", sign);
		StringBuffer url = new StringBuffer("weixin://wap/pay?");
		String paramUrl = StringUtil.toStringForUrl(data);
		url.append(URLEncoder.encode(paramUrl));
		return url.toString(); 
	}
	
	/**
	 * 生成wap支付数据
	 * @param prepayid
	 * @param wechatPayVo
	 * @return
	 */
	public static Map<String,String> genWapPayData(String prepayid, WechatPayVo wechatPayVo){
		Map<String,String> data = new TreeMap();
		data.put("appid", wechatPayVo.getAppid());
		data.put("noncestr", RandomStringUtils.random(32, RANDOM_STRING).toUpperCase());
		data.put("package", "WAP");
		data.put("prepayid", prepayid);
		data.put("timestamp", String.valueOf(System.currentTimeMillis()));
		data.put("sign", "");
		return data;
	}
	
	private static Map<String,String> nonceStrMap = new TreeMap<String,String>();
	/**
	 * 由于生成预支付订单 和 调起去付 的时候 nonceStr这个随机数要一致
	 * @param packageNum
	 * @return
	 */
	private static String getNonceStr(String packageNum){
		String value = nonceStrMap.get(packageNum);
		if(value==null || "".equals(value)){
			value = RandomStringUtils.random(32, WechatPayUtil.RANDOM_STRING).toUpperCase();
			nonceStrMap.put(packageNum, value);
		}
		return value;
	}
}
